#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>

#include <davint/davint.h>

#include "kbd.h"

#define UP		'A'
#define DOWN	'B'
#define RIGHT	'C'
#define LEFT	'D'

static signed int set_term();
signed int kbd_threadFunction();

static signed int set_term() {
    struct termios settings;
    signed int result;
    signed int desc;
    desc = open("/dev/tty0", O_RDONLY);
    result = tcgetattr(desc, &settings);
    if (result < 0) {
        perror("error in tcgetattr");
        close(desc);
        return 0;
    }
    settings.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
    settings.c_oflag &= ~OPOST;
    settings.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    settings.c_cflag &= ~(CSIZE | PARENB);
    settings.c_cflag |= CS8;
    settings.c_cc[VMIN] = 0;
    settings.c_cc[VTIME] = 1;
    result = tcsetattr(desc, TCSANOW, &settings);
    if (result < 0) {
        perror("error in tcsetattr");
        close(desc);
        return 0;
    }
    close(desc);
    return 1;
}

signed int kbd_threadFunction() {
    unsigned char key;
    event e;
    davint_event_queue_t *queue;

    while ((queue = listener_eventQueue_request()) == NULL);

    if (!set_term()) {
        fprintf(stderr, "Erro no set_term.\n");
    }

    enum state {
        KBD_STATE_DEFAULT,
        KBD_STATE_ESC_27,
        KBD_STATE_ESC_91
    };

    enum state kbd_key_state = KBD_STATE_DEFAULT;

    while (1) {
        if ((key = getchar()) != 255) {
            if (kbd_key_state == KBD_STATE_DEFAULT) {
                if (key == 27) {
                    kbd_key_state = KBD_STATE_ESC_27;
                    e.type = EVENT_NULL;
                    e.paramsize = 0;
                    e.paramp = NULL;
                } else {
                    if (isprint(key)) {
                        e.type = EVENT_ADD_CHAR;
                        e.paramsize = sizeof (key);
                        if (kbd_eParamp_malloc(&e) == KBD_FAILURE) {
                            return KBD_FAILURE;
                        }
                        memcpy(e.paramp, &key, e.paramsize);
                    } else {
                        switch (key) {
                            case KBD_KEYCODE_ENTER:
                                e.type = EVENT_ENTER;
                                e.paramsize = 0;
                                e.paramp = NULL;
                                break;
                            case KBD_KEYCODE_BACKSPACE:
                                e.type = EVENT_DEL_CHAR;
                                e.paramsize = 0;
                                e.paramp = NULL;
                                break;
                            default:
                                e.type = EVENT_NULL;
                        }
                    }
                }
            } else if (kbd_key_state == KBD_STATE_ESC_27) {
                if (key == '[') {
                    kbd_key_state = KBD_STATE_ESC_91;
                    e.type = EVENT_NULL;
                    e.paramsize = 0;
                    e.paramp = NULL;
                } else {
                    kbd_key_state = KBD_STATE_DEFAULT;
                }
            } else if (kbd_key_state == KBD_STATE_ESC_91) {
				switch (key) {
					case UP:
						e.type = EVENT_UP;
						e.paramsize = 0;
						e.paramp = NULL;
						break;
					case DOWN:
						e.type = EVENT_DOWN;
						e.paramsize = 0;
						e.paramp = NULL;
						break;
					case RIGHT:
						printf("rigth\n");
						e.type = EVENT_NULL;
						e.paramsize = 0;
						e.paramp = NULL;
						break;
					case LEFT:
						printf("left\n");
						e.type = EVENT_NULL;
						e.paramsize = 0;
						e.paramp = NULL;
						break;
					default:
						e.type = EVENT_NULL;
				}
                kbd_key_state = KBD_STATE_DEFAULT;
            } else {
                kbd_key_state = KBD_STATE_DEFAULT;
            }
        } else {
            if (kbd_key_state == KBD_STATE_ESC_27) { // tecla ESC

            } else {
                e.type = EVENT_NULL;
            }
        }

        if (e.type != EVENT_NULL) {
            unsigned int time;
            davint_event_enqueue(queue, (unsigned char *) & e, sizeof (e), time);
        }

        usleep(10000);
    }

    return KBD_SUCCESS;
}

signed int kbd_eParamp_malloc(event *e) {
    if ((e->paramp = malloc(e->paramsize)) == NULL) {
        fprintf(stderr, "Erro ao alocar memória para o parâmetro do evento!\n");
        return KBD_FAILURE;
    }

    return KBD_SUCCESS;
}
